﻿using System;
using System.Security.Cryptography;
using System.Text;

/// <summary>
/// md5散列结果
/// </summary>
public class Md5HashResult
{
    private string hashValue;
    private string salt;

    /// <summary>
    /// 散列值
    /// </summary>
    public string HashValue
    {
        get { return hashValue; }
        set { hashValue = value; }
    }

    /// <summary>
    /// salt
    /// </summary>
    public string Salt
    {
        get { return salt; }
        set { salt = value; }
    }
}

/// <summary>
/// MD5散列
/// </summary>
public class MD5Hash
{
    /// <summary>
    /// 对字符串进行散列操作
    /// </summary>
    /// <param name="plainText">明文</param>
    /// <returns></returns>
    public static Md5HashResult Hash(string plainText)
    {
        return Hash(plainText, CreateSalt(4, 8));
    }

    /// <summary>
    /// 对字符串进行散列操作
    /// </summary>
    /// <param name="plainText">明文</param>
    /// <param name="saltBytes">salt</param>
    /// <returns></returns>
    public static Md5HashResult Hash(string plainText, byte[] saltBytes)
    {
        byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
        HMACMD5 md5 = new HMACMD5(saltBytes);
        byte[] hashedBytes = md5.ComputeHash(plainBytes);
        Md5HashResult result = new Md5HashResult();
        result.HashValue = Convert.ToBase64String(hashedBytes);
        result.Salt = Convert.ToBase64String(saltBytes);
        return result;
    }

    /// <summary>
    /// 验证散列值是否一致
    /// </summary>
    /// <param name="plainText">明文</param>
    /// <param name="salt">salt</param>
    /// <param name="hashValue">散列值</param>
    /// <returns></returns>
    public static bool Verify(string plainText, string salt, string hashValue)
    {
        byte[] saltBytes = Convert.FromBase64String(salt);
        Md5HashResult result = Hash(plainText, saltBytes);
        return result.HashValue == hashValue;
    }

    /// <summary>
    /// 创建salt
    /// </summary>
    /// <param name="minSaltSize">最大长度</param>
    /// <param name="maxSaltSize">最小长度</param>
    /// <returns></returns>
    public static byte[] CreateSalt(int minSaltSize, int maxSaltSize)
    {
        Random random = new Random();
        int saltSize = random.Next(minSaltSize, maxSaltSize);
        byte[] saltBytes = new byte[saltSize];
        RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
        rng.GetNonZeroBytes(saltBytes);
        return saltBytes;
    }
}